////////////////////////////////////////////////////////////////////////////
//
//  CryEngine Source File.
//  Copyright (C), Crytek, 1999-2010.
// -------------------------------------------------------------------------
//  File name:	AssetModelDatabase.cpp
//  Version:	v1.00
//  Created:	15/04/2009 by Paulo Zaffari
//  Description:	Implements AssetModelDatabase.h
// -------------------------------------------------------------------------  
//  History:
//			15/04/2009	11:00 - Paulo Zaffari - created
//			15/03/2010	19:10 - Nicusor Nedelcu - refactored
//
//////////////////////////////////////////////////////////////////////////// 

#include "stdafx.h"
#include "AssetModelDatabase.h"
#include "AssetModelItem.h"
#include "Include/IAssetViewer.h"
#include "StringUtils.h"
#include "Util/IndexedFiles.h"

REGISTER_CLASS_DESC(CAssetModelDatabase);

CAssetModelDatabase::CAssetModelDatabase() : CAssetDisplayDatabase()
{
	// add fields
	static const int kFilenameColWidth = 150;
	static const int kFileSizeColWidth = 50;
	static const int kRelativePathColWidth = 50;
	static const int kRefCountColWidth = 30;
	static const int kTextureSizeColWidth = 50;
	static const int kTriangleCountColWidth = 50;
	static const int kVertexCountColWidth = 50;
	static const int kLodCountColWidth = 50;
	static const int kSubmeshCountColWidth = 50;
	static const int kPhysicsTriCountColWidth = 50;
	static const int kPhysicsSizeColWidth = 40;
	static const int kLODsTrisColWidth = 100;
	static const int kSplitLODsColWidth = 40;
	static const int kBBoxColWidth = 50;
	static const int kUsedInLevelColWidth = 40;
	static const int kLoadedInLevelColWidth = 40;
	static const int kDccFilenameColWidth = 50;
	static const int kTagsColWidth = 60;

	m_assetFields.push_back( SAssetField( "filename", "Filename", SAssetField::eAssetFieldType_String, kFilenameColWidth ) );
	m_assetFields.push_back( SAssetField( "filesize", "Filesize (Bytes)", SAssetField::eAssetFieldType_Int32, kFileSizeColWidth ) );
	m_assetFields.push_back( SAssetField( "relativepath", "Path", SAssetField::eAssetFieldType_String, kRelativePathColWidth ) );
	m_assetFields.push_back( SAssetField( "nref", "Refs", SAssetField::eAssetFieldType_Int32, kRefCountColWidth ) );
	m_assetFields.push_back( SAssetField( "texturesize", "Texture size (Bytes)", SAssetField::eAssetFieldType_Int32, kTextureSizeColWidth ) );
	m_assetFields.push_back( SAssetField( "trianglecount", "Triangles", SAssetField::eAssetFieldType_Int32, kTriangleCountColWidth ) );
	m_assetFields.push_back( SAssetField( "vertexcount", "Vertices", SAssetField::eAssetFieldType_Int32, kVertexCountColWidth ) );
	m_assetFields.push_back( SAssetField( "lodcount", "LODs", SAssetField::eAssetFieldType_Int32, kLodCountColWidth ) );
	m_assetFields.push_back( SAssetField( "submeshcount", "Submeshes", SAssetField::eAssetFieldType_Int32, kSubmeshCountColWidth ) );
	m_assetFields.push_back( SAssetField( "phytricount", "Physics tris", SAssetField::eAssetFieldType_Int32, kPhysicsTriCountColWidth ) );
	m_assetFields.push_back( SAssetField( "physize", "Physics size (Bytes)", SAssetField::eAssetFieldType_Int32, kPhysicsSizeColWidth ) );
	m_assetFields.push_back( SAssetField( "lodstricount", "LODs tris", SAssetField::eAssetFieldType_String, kLODsTrisColWidth ) );
	m_assetFields.push_back( SAssetField( "splitlods", "Split LODs", SAssetField::eAssetFieldType_Bool, kSplitLODsColWidth ) );
	m_assetFields.push_back( SAssetField( "bbox_x", "Size(X)", SAssetField::eAssetFieldType_Float, kBBoxColWidth ) );
	m_assetFields.push_back( SAssetField( "bbox_y", "Size(Y)", SAssetField::eAssetFieldType_Float, kBBoxColWidth ) );
	m_assetFields.push_back( SAssetField( "bbox_z", "Size(Z)", SAssetField::eAssetFieldType_Float, kBBoxColWidth ) );
	m_assetFields.push_back( SAssetField( "usedinlevel", "Used in level", SAssetField::eAssetFieldType_Bool, kUsedInLevelColWidth ) );
	m_assetFields.push_back( SAssetField( "loadedinlevel", "Loaded in level", SAssetField::eAssetFieldType_Bool, kLoadedInLevelColWidth ) );
	m_assetFields.push_back( SAssetField( "dccfilename", "DCC Filename", SAssetField::eAssetFieldType_String, kDccFilenameColWidth ) );
	m_assetFields.push_back( SAssetField( "tags", "Tags", SAssetField::eAssetFieldType_String, kTagsColWidth ) );
}

CAssetModelDatabase::~CAssetModelDatabase()
{
	// empty call FreeData() first
}

HRESULT STDMETHODCALLTYPE CAssetModelDatabase::QueryInterface( const IID &riid, void **ppvObj ) 
{ 
	if(riid == __uuidof(IAssetDisplayDatabase)/* && m_pIntegrator*/)
	{
		*ppvObj = this;
		return S_OK;
	}
	return E_NOINTERFACE ; 
}

ULONG STDMETHODCALLTYPE CAssetModelDatabase::AddRef()
{
	return ++m_ref;
};

ULONG STDMETHODCALLTYPE CAssetModelDatabase::Release() 
{ 
	if( (--m_ref) == 0 )
	{
		FreeData();
		delete this;
		return 0; 
	}
	else
		return m_ref;
}

const char* CAssetModelDatabase::GetDatabaseTypeExt() const
{
	return "cgf"; 
};

const char* CAssetModelDatabase::GetDatabaseName() const
{
	return "Models";
}

void CAssetModelDatabase::Refresh()
{
	FreeData();

	CFileUtil::FileArray	cFiles;
	int										nTotalFiles = 0;
	int										nCurrentFile = 0;
	CString								strPathOnly, strFileNameOnly;

	// search for models
	std::vector<CString> tags; tags.push_back("cgf");
	CIndexedFiles::GetDB().GetFilesWithTags(cFiles, tags);

	nTotalFiles = cFiles.size();

	for( nCurrentFile = 0; nCurrentFile < nTotalFiles; ++nCurrentFile )
	{
		CFileUtil::FileDesc& rstFileDescriptor = cFiles[nCurrentFile];
		CString strIntermediateFilename = rstFileDescriptor.filename.GetBuffer();
		strIntermediateFilename.MakeLower();
		Path::ConvertBackSlashToSlash(strIntermediateFilename);
		CString strOutputModelName = strIntermediateFilename;
		if( Path::GetExt(strOutputModelName) != "cgf" )
			continue;

		// No need to load files already loaded by the engine...
		if( m_assets.find( strOutputModelName.GetBuffer() ) != m_assets.end() )
		{
			continue;
		}

		CString strExtension = Path::GetExt( strOutputModelName.GetBuffer() );
		const char* szModelName = strOutputModelName.GetBuffer();

		if( !szModelName )
		{
			continue;
		}

		strFileNameOnly = Path::GetFile( strOutputModelName );
		strPathOnly = Path::GetPath( strOutputModelName );

		CAssetModelItem* poModelDatabaseItem = new CAssetModelItem();

		poModelDatabaseItem->SetFileSize( rstFileDescriptor.size );
		poModelDatabaseItem->SetFilename( strFileNameOnly );
		poModelDatabaseItem->SetRelativePath( strPathOnly );
		poModelDatabaseItem->SetOwnerDisplayDatabase( this );
		poModelDatabaseItem->SetFileExtension( strExtension );
		poModelDatabaseItem->SetFlag( IAssetDisplay::eAssetFlags_Visible, true );
		poModelDatabaseItem->CheckIfItsLod();
		m_assets[strOutputModelName] = poModelDatabaseItem;
	}
}

void CAssetModelDatabase::CacheFieldsInfoForAlreadyLoadedAssets()
{
	ISystem *pSystem = GetISystem();
	I3DEngine *p3DEngine = pSystem->GetI3DEngine();

	// iterate through all IStatObj
	int nObjCount = 0;

	p3DEngine->GetLoadedStatObjArray(0,nObjCount);

	if (nObjCount > 0)
	{
		std::vector<IStatObj*> pObjects;
		pObjects.resize(nObjCount);
		p3DEngine->GetLoadedStatObjArray(&pObjects[0],nObjCount);
		
		for (int nCurObj = 0; nCurObj < nObjCount; nCurObj++)
		{
			CString statObjName = pObjects[nCurObj]->GetFilePath();
			Path::ConvertSlashToBackSlash(statObjName);
			TFilenameAssetMap::iterator itr = m_assets.find(statObjName.GetBuffer());
			if ( m_assets.end() != itr )
			{
				itr->second->SetFlag( IAssetDisplay::eAssetFlags_UsedInLevel, true );
				static_cast<CAssetModelItem*>(itr->second)->CacheFieldsInfoForLoadedStatObj(pObjects[nCurObj]);
			}
		}
	}
}

const char* CAssetModelDatabase::GetTransactionFilename() const
{
	return "modelAssetTransactions.xml";
}

void CAssetModelDatabase::PrecacheFieldsInfoFromFileDB(const XmlNodeRef& db)
{
	assert(db->isTag(GetDatabaseName()));
	for(int i=0; i<db->getChildCount(); ++i)
	{
		XmlNodeRef entry = db->getChild(i);
		CString fileName = entry->getAttr("fileName");
		TFilenameAssetMap::iterator assetIt = m_assets.find(fileName);
		bool bAssetFound = m_assets.end() != assetIt;
		if(bAssetFound)
			assetIt->second->FromXML(entry);
	}
}
